package com.peas.platform.common.core.controller.vo;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryUsage;

@Slf4j
public class MemoryInfo {

    // usedMemory 是heap使用内存 (eden+survivor+old)
    @Getter
    private final Long usedMemory;

    // maxMemory 是heap最大内存
    @Getter
    private final Long maxMemory;

    // usedOldGen "Old Gen"使用内存
    @Getter
    private final Long usedOldGen;

    // maxOldGen "Old Gen"最大内存
    @Getter
    private final Long maxOldGen;

    // usedPermGen "Perm Gen"使用内存
    @Getter
    private final Long usedPermGen;

    // maxPermGen "Perm Gen"最大内存
    @Getter
    private final Long maxPermGen;

    // usedEdenSpace "Eden Space"使用内存
    @Getter
    private final Long usedEdenSpace;

    // maxEdenSpace "Eden Space"最大内存
    @Getter
    private final Long maxEdenSpace;

    // usedSurvivorSpace "Survivor Space"使用内存
    @Getter
    private final Long usedSurvivorSpace;

    // maxSurvivorSpace "Survivor Space"最大内存
    @Getter
    private final Long maxSurvivorSpace;

    @Getter
    private final Long usedNonHeapMemory;

    @Getter
    private final Long maxNonHeapMemory;

    private final MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();

    private static final String DIRECT_BUFFER_MBEAN = "java.nio:type=BufferPool,name=direct";

    private static final String MAPPED_BUFFER_MBEAN = "java.nio:type=BufferPool,name=mapped";

    public MemoryInfo() {
        usedMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
        maxMemory = Runtime.getRuntime().maxMemory();
        final MemoryPoolMXBean permGenMemoryPool = getPermGenMemoryPool();
        if (permGenMemoryPool != null) {
            final MemoryUsage usage = permGenMemoryPool.getUsage();
            usedPermGen = usage.getUsed();
            maxPermGen = usage.getMax();
        } else {
            usedPermGen = 0L;
            maxPermGen = 0L;
        }
        final MemoryPoolMXBean oldGenMemoryPool = getOldGenMemoryPool();
        if (oldGenMemoryPool != null) {
            final MemoryUsage usage = oldGenMemoryPool.getUsage();
            usedOldGen = usage.getUsed();
            maxOldGen = usage.getMax();
        } else {
            usedOldGen = 0L;
            maxOldGen = 0L;
        }
        final MemoryPoolMXBean edenSpaceMemoryPool = getEdenSpacePool();
        if (edenSpaceMemoryPool != null) {
            final MemoryUsage usage = edenSpaceMemoryPool.getUsage();
            usedEdenSpace = usage.getUsed();
            maxEdenSpace = usage.getMax();
        } else {
            usedEdenSpace = 0L;
            maxEdenSpace = 0L;
        }
        final MemoryPoolMXBean survivorSpacePool = getSurvivorSpaceMemoryPool();
        if (survivorSpacePool != null) {
            final MemoryUsage usage = survivorSpacePool.getUsage();
            usedSurvivorSpace = usage.getUsed();
            maxSurvivorSpace = usage.getMax();
        } else {
            usedSurvivorSpace = 0L;
            maxSurvivorSpace = 0L;
        }
        final MemoryUsage nonHeapMemoryUsage = ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage();
        usedNonHeapMemory = nonHeapMemoryUsage.getUsed();
        maxNonHeapMemory = nonHeapMemoryUsage.getMax();
    }

    private MemoryPoolMXBean getEdenSpacePool() {
        for (final MemoryPoolMXBean memoryPool : ManagementFactory.getMemoryPoolMXBeans()) {
            if (memoryPool.getName().endsWith("Eden Space")) {
                return memoryPool;
            }
        }
        return null;
    }

    private MemoryPoolMXBean getOldGenMemoryPool() {
        for (final MemoryPoolMXBean memoryPool : ManagementFactory.getMemoryPoolMXBeans()) {
            if (memoryPool.getName().endsWith("Old Gen")) {
                return memoryPool;
            }
        }
        return null;
    }

    private MemoryPoolMXBean getPermGenMemoryPool() {
        for (final MemoryPoolMXBean memoryPool : ManagementFactory.getMemoryPoolMXBeans()) {
            if (memoryPool.getName().endsWith("Perm Gen")) {
                return memoryPool;
            }
        }
        return null;
    }

    private MemoryPoolMXBean getSurvivorSpaceMemoryPool() {
        for (final MemoryPoolMXBean memoryPool : ManagementFactory.getMemoryPoolMXBeans()) {
            if (memoryPool.getName().endsWith("Survivor Space")) {
                return memoryPool;
            }
        }
        return null;
    }

    public long getUsedDirectBufferSize() {
        long directBufferSize = 0;
        try {
            ObjectName directPool = new ObjectName(DIRECT_BUFFER_MBEAN);
            directBufferSize = (Long) mbeanServer.getAttribute(directPool, "MemoryUsed");
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return directBufferSize;
    }

    public double getUsedEdenSpacePercentage() {
        if (usedEdenSpace > 0 && maxEdenSpace > 0) {
            return 100d * usedEdenSpace / maxEdenSpace;
        }
        return 0d;
    }

    public long getUsedMappedSize() {
        long mappedBufferSize = 0;
        try {
            ObjectName directPool = new ObjectName(MAPPED_BUFFER_MBEAN);
            mappedBufferSize = (Long) mbeanServer.getAttribute(directPool, "MemoryUsed");
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return mappedBufferSize;
    }

    public double getUsedMemoryPercentage() {
        return 100d * usedMemory / maxMemory;
    }

    public double getUsedNonHeapPercentage() {
        if (usedNonHeapMemory > 0 && maxNonHeapMemory > 0) {
            return 100d * usedNonHeapMemory / maxNonHeapMemory;
        }
        return 0d;
    }

    public double getUsedOldGenPercentage() {
        if (usedOldGen > 0 && maxOldGen > 0) {
            return 100d * usedOldGen / maxOldGen;
        }
        return 0d;
    }

    public double getUsedPermGenPercentage() {
        if (usedPermGen > 0 && maxPermGen > 0) {
            return 100d * usedPermGen / maxPermGen;
        }
        return 0d;
    }

    public double getUsedSurvivorSpacePercentage() {
        if (usedSurvivorSpace > 0 && maxSurvivorSpace > 0) {
            return 100d * usedSurvivorSpace / maxSurvivorSpace;
        }
        return 0d;
    }

    @Override
    public String toString() {
        return getClass().getSimpleName() + "[usedMemory=" + getUsedMemory() + ", maxMemory=" + getMaxMemory() + ']';
    }

}
